package org.jeecg.modules.flow.aspect;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.modules.dock.config.JsonUtils;
import org.jeecg.modules.dock.constant.SystemConstants;
import org.jeecg.modules.dock.entity.BaseResponseV0;
import org.jeecg.modules.dock.entity.dto.response.SapBaseResponseDTO;
import org.jeecg.modules.dock.enums.DockDataStatus;
import org.jeecg.modules.flow.record.entity.FlowRequestLog;
import org.jeecg.modules.flow.record.mapper.FlowRequestLogMapper;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;


/**
 * @Author scott
 * @email jeecgos@163.com
 * @Date 2018年1月14日
 */
@Aspect
@Component
public class FlowLogAspect {

    @Resource
    private FlowRequestLogMapper flowRequestLogMapper;

    @Pointcut("@annotation(org.jeecg.modules.flow.aspect.FlowLog)")
    public void logPointCut() {

    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        Object result = null;
        try {
            result = point.proceed();
        } catch (Exception e) {
            result = Result.error(e.getMessage());
        } finally {
            //保存日志
            saveSysLog(point, result);
        }

        return result;
    }

    private void saveSysLog(ProceedingJoinPoint joinPoint, Object result) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        FlowLog syslog = method.getAnnotation(FlowLog.class);
        FlowRequestLog receiveLog = new FlowRequestLog();
        if (StringUtils.isBlank(receiveLog.getUuid())) {
            receiveLog.setUuid(IdUtil.fastSimpleUUID());
        }
        receiveLog.setTarget(SystemConstants.WMS);
        receiveLog.setSource(syslog.source());

        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        receiveLog.setInterfaceName(className + "." + methodName + "()");
        //获取request
        HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
        //请求的参数
        String reqestParams = getReqestParams(request, joinPoint, syslog);
        receiveLog.setData(reqestParams);
        receiveLog.setReceiveTime(new Date());
        if (result != null) {
            receiveLog.setResultData(JsonUtils.toJson(result));
        }
        DockDataStatus status = getResult(result);
        receiveLog.setStatus(status);
        receiveLog.setResultCode(status.toString());

        if (status.getCode().equals("E")) {
            flowRequestLogMapper.insert(receiveLog);
        }
    }

    /**
     * @param request:   request
     * @param joinPoint: joinPoint
     * @Description: 获取请求参数
     * @author: scott
     * @date: 2020/4/16 0:10
     * @Return: java.lang.String
     */
    private String getReqestParams(HttpServletRequest request, JoinPoint joinPoint, FlowLog dockLog) {
        String httpMethod = request.getMethod();
        int index = dockLog.index();

        String params = "";
        if (CommonConstant.HTTP_POST.equals(httpMethod) || CommonConstant.HTTP_PUT.equals(httpMethod) || CommonConstant.HTTP_PATCH.equals(httpMethod)) {
            Object[] paramsArray = joinPoint.getArgs();
            // java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
            //  https://my.oschina.net/mengzhang6/blog/2395893
            Object arguments = new Object();
            if (paramsArray.length >= index) {
                arguments = paramsArray[index];
            } else {
                arguments = paramsArray[0];
            }
            params = JSON.toJSONString(arguments, SerializerFeature.IgnoreErrorGetter);

            //update-end-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
        } else {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            // 请求的方法参数值
            Object[] args = joinPoint.getArgs();
            // 请求的方法参数名称
            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
            String[] paramNames = u.getParameterNames(method);
            if (args != null && paramNames != null) {
                for (int i = 0; i < args.length; i++) {
                    params += "  " + paramNames[i] + ": " + args[i];
                }
            }
        }
        return params;
    }

    /**
     * @param obj
     * @return
     */
    private DockDataStatus getResult(Object obj) {

        DockDataStatus rest = DockDataStatus.FAILURE;
        if (obj == null) {
            return rest;
        }
        if (obj instanceof BaseResponseV0) {
            BaseResponseV0 res = (BaseResponseV0) obj;
            if (BaseResponseV0.SUCCESS.equals(res.getTarget())) {
                rest = DockDataStatus.SUCCESS;
            }
        } else if (obj instanceof List) {
            List<SapBaseResponseDTO> res = (List<SapBaseResponseDTO>) obj;
            if (CollectionUtil.isNotEmpty(res)) {
                if (SapBaseResponseDTO.SUCCESS.equals(res.get(0).getResult())) {
                    rest = DockDataStatus.SUCCESS;
                }
            }
        } else if (obj instanceof Result) {
            Result res = (Result) obj;
            if (res.isSuccess()) {
                rest = DockDataStatus.SUCCESS;
            }
        }
        return rest;
    }
}
